using System;
using System.Collections.Generic;
using System.Linq;
using PlasticPipe.PlasticProtocol.Client.Proxies;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;

[CustomEditor(typeof(ProbeVisualizer))]
public class ProbeVisualizerEditor : Editor
{
    ProbeVisualizer vis;
    
    void OnEnable() => vis = (ProbeVisualizer)target;

    public override void OnInspectorGUI()
    {
        if (target == null)
        {
            return;
        }

        serializedObject.Update();

        UnityLightProbeOption();

        EditorGUILayout.Space();
        
        CompressLightProbe();
        
        EditorGUILayout.Space();
        
        VisualObject();
        
        EditorGUILayout.Space();

        VisualObjectToggle();
        
        EditorGUILayout.Space();
        
        SceneView.RepaintAll();
        serializedObject.ApplyModifiedProperties();
    }
    
    private void UnityLightProbeOption()
    {
        GUILayout.Label("原始光照探针生成", new GUIStyle(EditorStyles.boldLabel));

        SerializedProperty serializedLightProbeGroup = serializedObject.FindProperty("lightProbeGroup");
        SerializedProperty lightProbeCountInGroup = serializedObject.FindProperty("lightProbeCountInGroup");
        
        EditorGUILayout.PropertyField(serializedLightProbeGroup);
        EditorGUILayout.PropertyField(lightProbeCountInGroup);
        
        if (GUILayout.Button("生成LightProbe"))
        {
            LightProbeGroup lightProbeGroup = (LightProbeGroup)serializedLightProbeGroup.boxedValue;
            if (lightProbeGroup == null)
            {
                return;
            }

            int count = lightProbeCountInGroup.intValue;
            Vector3[] pos = new Vector3[count * count * count];

            int index = 0;
            for (int x = 0; x < count; x++)
            {
                for (int y = 0; y < count; y++)
                {
                    for (int z = 0; z < count; z++)
                    {
                        pos[index++] = new Vector3(x, y, z);
                    }
                }
            }
            lightProbeGroup.probePositions = pos;
        }
    }

    private void VisualObject()
    {
        GUILayout.Label("根据LightProbe生成可视化对象", new GUIStyle(EditorStyles.boldLabel));
        
        SerializedProperty serializedVisualGORoot = serializedObject.FindProperty("visualGORoot");
        GameObject visualGORoot = serializedVisualGORoot.boxedValue as GameObject;

        SerializedProperty serializedVisualGOTemp = serializedObject.FindProperty("visualGOTemp");
        GameObject visualGOTemp = serializedVisualGOTemp.boxedValue as GameObject;
        
        EditorGUILayout.PropertyField(serializedVisualGORoot);
        EditorGUILayout.PropertyField(serializedVisualGOTemp);
        
        if (GUILayout.Button("生成可视化对象"))
        {
            SerializedProperty serializedLightProbeGroup = serializedObject.FindProperty("lightProbeGroup");
            LightProbeGroup lightProbeGroup = (LightProbeGroup)serializedLightProbeGroup.boxedValue;
            
            // for (int i = 0; i < visualGORoot.transform.childCount;)
            // {
            //     GameObject.Destroy(vis.transform.GetChild(i));
            // }
            
            for (int i = 0; i < lightProbeGroup.probePositions.Length; i++)
            {
                GameObject go = GameObject.Instantiate(visualGOTemp, visualGORoot.transform, true);
                go.transform.localPosition = lightProbeGroup.probePositions[i];
                go.transform.localRotation = Quaternion.identity;
                go.transform.localScale = Vector3.one;

                lightProbeGroup.probePositions[i] = visualGORoot.transform.localPosition;
            }
        }
    }
    
    private void CompressLightProbe()
    {
        GUILayout.Label("Compress Settings(需要先烘培LightProbe)", EditorStyles.boldLabel);
        
        EditorGUILayout.PropertyField(serializedObject.FindProperty("irradianceVolumeData"));
        
        if (GUILayout.Button("Compress and Save"))
        {
            IrradianceVolumeData.CompressAndSave();
        }
    }

    private void VisualObjectToggle()
    {
        GUILayout.Label("可视化显示", new GUIStyle(EditorStyles.boldLabel));
        
        SerializedProperty serializedVisualGORoot = serializedObject.FindProperty("visualGORoot");
        GameObject visualGORoot = serializedVisualGORoot.boxedValue as GameObject;
        
        SerializedProperty serializedSampleDirection = serializedObject.FindProperty("sampleDirection");
        SerializedProperty serializedChannel = serializedObject.FindProperty("channel");
        
        EditorGUILayout.PropertyField(serializedSampleDirection);
        EditorGUILayout.PropertyField(serializedChannel);
        
        //Unity 的采样方法 
        EditorGUILayout.BeginHorizontal();
        
        if (GUILayout.Button("Show Unity Light Probe Full"))
        {
            MeshRenderer[] mrs = visualGORoot.GetComponentsInChildren<MeshRenderer>();
            for (int i = 0; i < mrs.Length; i++)
            {
                var col = SHUtility.EvaluateFullV(LightmapSettings.lightProbes.bakedProbes[i], vis.GetSampleDirection());
                //col = ProbeVisualizer.FilterChannel(col, vis.channel);

                MaterialPropertyBlock mpb = new MaterialPropertyBlock();
                mpb.SetInt("_UseLightProbe_Full", 1);
                // mpb.SetVector("_Color", col);
                mrs[i].SetPropertyBlock(mpb);
            }
        }
        
        if (GUILayout.Button("Show Unity Light Probe L0L1"))
        {
            MeshRenderer[] mrs = visualGORoot.GetComponentsInChildren<MeshRenderer>();
            for (int i = 0; i < mrs.Length; i++)
            {
                var col = SHUtility.EvaluateL0L1_Vector3(LightmapSettings.lightProbes.bakedProbes[i], vis.GetSampleDirection());
                //col = ProbeVisualizer.FilterChannel(col, vis.channel);
                
                MaterialPropertyBlock mpb = new MaterialPropertyBlock();
                mpb.SetInt("_UseLightProbe_L0L1", 1);
                mpb.SetVector("_Color", col);
                mrs[i].SetPropertyBlock(mpb);
            }
        }
        
        if (GUILayout.Button("Show Unity Light Probe L1"))
        {
            MeshRenderer[] mrs = visualGORoot.GetComponentsInChildren<MeshRenderer>();
            for (int i = 0; i < mrs.Length; i++)
            {
                var col = SHUtility.EvaluateL1_Vector3(LightmapSettings.lightProbes.bakedProbes[i], vis.GetSampleDirection());
                
                MaterialPropertyBlock mpb = new MaterialPropertyBlock();
                mpb.SetInt("_UseLightProbe_L1", 1);
                mpb.SetVector("_Color", col);
                mrs[i].SetPropertyBlock(mpb);
            }
        }
        
        if (GUILayout.Button("Show Unity Light Probe L0"))
        {
            MeshRenderer[] mrs = visualGORoot.GetComponentsInChildren<MeshRenderer>();
            for (int i = 0; i < mrs.Length; i++)
            {
                // var col = SHUtility.EvaluateL0(LightmapSettings.lightProbes.bakedProbes[i]);
                // col = ProbeVisualizer.FilterChannel(col, vis.channel);
                var col = SHUtility.EvaluateL0_Vector3 (LightmapSettings.lightProbes.bakedProbes[i]);
                MaterialPropertyBlock mpb = new MaterialPropertyBlock();
                mpb.SetInt("_UseLightProbe_L0", 1);
                mpb.SetVector("_Color", col);
                mrs[i].SetPropertyBlock(mpb);
            }
        }
        
        EditorGUILayout.EndHorizontal();
        
        EditorGUILayout.BeginHorizontal();

        if (GUILayout.Button("Show IVGI L0L1"))
        {
            ShowIVGI(visualGORoot, 1, 0);
        }
        
        if (GUILayout.Button("Show IVGI L1"))
        {
            ShowIVGI(visualGORoot, 0, 1);      
        }
        
        EditorGUILayout.EndHorizontal();
        
        EditorGUILayout.BeginHorizontal();

        if (GUILayout.Button("Show IVGI(Tex) L0L1"))
        {
            ShowIVGI_Tex(visualGORoot, 1, 0);
        }
        
        if (GUILayout.Button("Show IVGI(Tex) L1"))
        {
            ShowIVGI_Tex(visualGORoot, 0, 1);      
        }
        
        EditorGUILayout.EndHorizontal();

        // EditorGUILayout.BeginHorizontal();
        //
        // if (GUILayout.Button("Show Unity Light Probe L0 + Luminance [1]"))
        // {
        //     float[] grid = new float[1000];
        //     Vector4[] color = new Vector4[1000];
        //     Vector4[] luminance = new Vector4[1000];
        //
        //     int colorIndex = 0;
        //     
        //     MeshRenderer[] mrs = visualGORoot.GetComponentsInChildren<MeshRenderer>();
        //     for (int i = 0; i < mrs.Length; i++, colorIndex++)
        //     {
        //         var col = SHUtility.EvaluateL0_Vector3 (LightmapSettings.lightProbes.bakedProbes[i]);
        //         int x = (int)mrs[i].transform.position.x;
        //         int y = (int)mrs[i].transform.position.y;
        //         int z = (int)mrs[i].transform.position.z;
        //
        //         grid[x * 10 * 10 + y * 10 + z] = colorIndex;
        //         color[colorIndex] = new Vector3(col.x, col.y, col.z);
        //         luminance[colorIndex] = SHUtility.EvaluateL1Luminance(LightmapSettings.lightProbes.bakedProbes[i], vis.GetSampleDirection());
        //         
        //         MaterialPropertyBlock mpb = new MaterialPropertyBlock();
        //         mpb.SetInt("_OnlyColor", 1);
        //         mpb.SetVector("_Color", luminance[colorIndex]);
        //         mpb.SetInt("_DataIndex", colorIndex);
        //         mpb.SetVector("_CenterPosition", mrs[i].transform.position);
        //         mpb.SetInt("_Use_IVGI_L0L1", 1);
        //         mpb.SetInt("_Use_IVGI_L1", 0);
        //         
        //         mrs[i].SetPropertyBlock(mpb);
        //     }
        //     
        //     
        //     Shader.SetGlobalVector("_GridOrigin", new Vector3(0,0,0));
        //     Shader.SetGlobalInt("_GridDim", 10);
        //     Shader.SetGlobalFloat("_VoxelSize", 1);
        //     
        //     Shader.SetGlobalFloatArray("_IVGIIndexGrid", grid.ToList());
        //     Shader.SetGlobalVectorArray("_IVGIDataBuffer_L0", color.ToList());
        //     Shader.SetGlobalVectorArray("_IVGIDataBuffer_Luminance", luminance.ToList());
        //     
        // }
        //
        // if (GUILayout.Button("Show Unity Light Probe Luminance [1]"))
        // {
        //     float[] grid = new float[1000];
        //     Vector4[] color = new Vector4[1000];
        //     Vector4[] luminance = new Vector4[1000];
        //
        //     int colorIndex = 0;
        //     
        //     MeshRenderer[] mrs = visualGORoot.GetComponentsInChildren<MeshRenderer>();
        //     for (int i = 0; i < mrs.Length; i++, colorIndex++)
        //     {
        //         var col = SHUtility.EvaluateL0_Vector3 (LightmapSettings.lightProbes.bakedProbes[i]);
        //         int x = (int)mrs[i].transform.position.x;
        //         int y = (int)mrs[i].transform.position.y;
        //         int z = (int)mrs[i].transform.position.z;
        //
        //         grid[x * 10 * 10 + y * 10 + z] = colorIndex;
        //         color[colorIndex] = new Vector3(col.x, col.y, col.z);
        //         luminance[colorIndex] = SHUtility.EvaluateL1Luminance(LightmapSettings.lightProbes.bakedProbes[i], vis.GetSampleDirection());
        //
        //         // PackSkyVis_L0L1RGB_FromEncodedLum(color[colorIndex],luminance[colorIndex],out Vector3 c,out Vector3 l);
        //         //
        //         // color[colorIndex] = c;
        //         // luminance[colorIndex] = l;
        //         
        //         MaterialPropertyBlock mpb = new MaterialPropertyBlock();
        //         mpb.SetInt("_OnlyColor", 1);
        //         mpb.SetVector("_Color", luminance[colorIndex]);
        //         mpb.SetInt("_DataIndex", colorIndex);
        //         mpb.SetVector("_CenterPosition", mrs[i].transform.position);
        //         mpb.SetInt("_Use_IVGI_L0L1", 0);
        //         mpb.SetInt("_Use_IVGI_L1", 1);
        //         
        //         mrs[i].SetPropertyBlock(mpb);
        //     }
        //     
        //     Shader.SetGlobalVector("_GridOrigin", new Vector3(0,0,0));
        //     Shader.SetGlobalInt("_GridDim", 10);
        //     Shader.SetGlobalFloat("_VoxelSize", 1);
        //     
        //     Shader.SetGlobalFloatArray("_IVGIIndexGrid", grid.ToList());
        //     Shader.SetGlobalVectorArray("_IVGIDataBuffer_L0", color.ToList());
        //     Shader.SetGlobalVectorArray("_IVGIDataBuffer_Luminance", luminance.ToList());
        //     
        // }
        //
        // EditorGUILayout.EndHorizontal();
    }

    void ShowIVGI(GameObject visualGORoot, int _Use_IVGI_L0L1, int _Use_IVGI_L1)
    {
        float[] grid = new float[1000];
        Vector4[] color = new Vector4[1000];
        Vector4[] luminance = new Vector4[1000];

        int colorIndex = 0;
            
        MeshRenderer[] mrs = visualGORoot.GetComponentsInChildren<MeshRenderer>();
        for (int i = 0; i < mrs.Length; i++, colorIndex++)
        {
            var col = SHUtility.EvaluateL0_Vector3(LightmapSettings.lightProbes.bakedProbes[i]);
            int x = (int)mrs[i].transform.position.x;
            int y = (int)mrs[i].transform.position.y;
            int z = (int)mrs[i].transform.position.z;

            grid[x * 10 * 10 + y * 10 + z] = colorIndex;
            color[colorIndex] = new Vector3(col.x, col.y, col.z);
            luminance[colorIndex] = SHUtility.EvaluateL1Luminance(LightmapSettings.lightProbes.bakedProbes[i]);
            // col = ProbeVisualizer.FilterChannel(col, vis.channel);

            MaterialPropertyBlock mpb = new MaterialPropertyBlock();
            mpb.SetInt("_OnlyColor", 1);
            mpb.SetVector("_Color", luminance[colorIndex]);
            mpb.SetInt("_DataIndex", colorIndex);
            mpb.SetVector("_CenterPosition", mrs[i].transform.position);
            mpb.SetInt("_Use_IVGI_L0L1", _Use_IVGI_L0L1);
            mpb.SetInt("_Use_IVGI_L1", _Use_IVGI_L1);
            mrs[i].SetPropertyBlock(mpb);
        }
            
        Shader.SetGlobalVector("_GridOrigin", new Vector3(0,0,0));
        Shader.SetGlobalInt("_GridDim", 10);
        Shader.SetGlobalFloat("_VoxelSize", 1);
            
        Shader.SetGlobalFloatArray("_IVGIIndexGrid", grid.ToList());
        Shader.SetGlobalVectorArray("_IVGIDataBuffer_L0", color.ToList());
        Shader.SetGlobalVectorArray("_IVGIDataBuffer_Luminance", luminance.ToList());
    }

    public static int textureSize = 100;
    public static int voxelSize = 1;
    public Texture3D mainTexture_0 = null;
    public Texture3D mainTexture_1 = null;

    public Vector3Int mainTextureTexelSize ;
    
    void ShowIVGI_Tex(GameObject visualGORoot, int _Use_IVGI_L0L1, int _Use_IVGI_L1)
    {
        mainTextureTexelSize = new Vector3Int(textureSize / voxelSize, textureSize / voxelSize, textureSize / voxelSize);
        
        TextureFormat mainTextureFormat = TextureFormat.RGBAHalf;
        mainTexture_0 = new Texture3D(mainTextureTexelSize.x, mainTextureTexelSize.y, mainTextureTexelSize.z, mainTextureFormat, false);
        mainTexture_0.filterMode = FilterMode.Trilinear;
        mainTexture_0.wrapMode = TextureWrapMode.Clamp;
        mainTexture_0.anisoLevel = 3;
        mainTexture_1 = new Texture3D(mainTextureTexelSize.x, mainTextureTexelSize.y, mainTextureTexelSize.z, mainTextureFormat, false);
        mainTexture_1.filterMode = FilterMode.Trilinear;
        mainTexture_1.wrapMode = TextureWrapMode.Clamp;

        CreateIrradianceVolume();
        //CreateIrradianceVolume1();

        MeshRenderer[] mrs = visualGORoot.GetComponentsInChildren<MeshRenderer>();
        for (int i = 0; i < mrs.Length; i++)
        {
            MaterialPropertyBlock mpb = new MaterialPropertyBlock();
            mpb.SetVector("_CenterPosition", mrs[i].transform.position);
            mpb.SetInt("_Use_IVGI_L0L1_Tex", _Use_IVGI_L0L1);
            mpb.SetInt("_Use_IVGI_L1_Tex", _Use_IVGI_L1);
            mrs[i].SetPropertyBlock(mpb);
        }
        
        Shader.SetGlobalVector("_GridOrigin", new Vector3(0,0,0));
        Shader.SetGlobalInt("_GridDim", textureSize/ voxelSize);
        Shader.SetGlobalFloat("_VoxelSize", voxelSize);

        Shader.SetGlobalTexture("_IVGIDataBuffer_L0_Tex", mainTexture_0);
        Shader.SetGlobalTexture("_IVGIDataBuffer_Luminance_Tex", mainTexture_1);
    }

    void CreateIrradianceVolume1()
    {
        int count = LightmapSettings.lightProbes.bakedProbes.Length;
        SphericalHarmonicsL2[] shs = LightmapSettings.lightProbes.bakedProbes;
        Vector3[] poss =  LightmapSettings.lightProbes.positions;
    
        int dim = textureSize / voxelSize; // 真实的 grid 维度
        mainTextureTexelSize = new Vector3Int(dim, dim, dim);
        
        for (int i = 0; i < textureSize; i++)
        {
            for (int ii = 0; ii < textureSize; ii++)
            {
                for (int iii = 0; iii < textureSize; iii++)
                {
                    // mainTexture_0.SetPixel(i, ii, iii, Color.black);
                    // mainTexture_1.SetPixel(i, ii, iii, Color.black);
                    // mainTexture_0.SetPixel(i, ii, iii, new Color(i/100.0f,ii/100.0f,iii/100.0f));
                    // mainTexture_1.SetPixel(i, ii, iii, Color.black);
                    var lightprobes = new UnityEngine.Rendering.SphericalHarmonicsL2[1];
                    var occlusionprobes = new Vector4[1];
                    Vector3[] positions = new Vector3[1] { new Vector3(i,ii,iii) };
                    LightProbes.CalculateInterpolatedLightAndOcclusionProbes(positions, lightprobes, occlusionprobes);
                    
                    SphericalHarmonicsL2 sh = lightprobes[0];
                    
                    Color color =  SHUtility.EvaluateL0(sh);
                    mainTexture_0.SetPixel(i , ii , iii , color);
                    
                    Vector3 luminance = SHUtility.EvaluateL1Luminance(sh);
                    mainTexture_1.SetPixel(i, ii, iii , new Color(luminance.x, luminance.y, luminance.z));
                }
            }
        }
    
        mainTexture_0.Apply();
        mainTexture_1.Apply();
    }

    void CreateIrradianceVolume()
    {
        //Bounds bounds = new Bounds(new Vector3(0, 0, 0), new Vector3(100, 100, 100));
        Bounds bounds = new Bounds(Vector3.one * 50f, new Vector3(100, 100, 100));
        SphericalHarmonicsL2[,,] shs = SampleSHVolumeGrid(bounds, 1);
        //shs = SmoothSHVolume(shs, true);
    
        for (int i = 0; i < 5; i++) // 迭代3次
        {
            shs = SmoothSHVolume555(shs, (from, to) =>
            {
                Vector3 dir = to - from;
                return !Physics.Raycast(from + Vector3.one * 0.1f, dir.normalized, dir.magnitude);
            },true);
        }
        
        for (int x = 0; x < 100; x++)
        for (int y = 0; y < 100; y++)
        for (int z = 0; z < 100; z++)
        {
            SphericalHarmonicsL2 sh = shs[x, y, z];
            var L0 = SHUtility.EvaluateL0(sh);
            var L1 = SHUtility.EvaluateL1Luminance(sh); // [-1,1]
            
            mainTexture_0.SetPixel(x, y, z, L0);
            mainTexture_1.SetPixel(x, y, z, new Color(L1.x * 0.5f + 0.5f, L1.y * 0.5f + 0.5f, L1.z * 0.5f + 0.5f));
        }
        
        mainTexture_0.Apply();
        mainTexture_1.Apply();

        mainTexture_0 = ResizeTexture3D(mainTexture_0, 50, 50, 50);
    }
    
    public static Texture3D ResizeTexture3D(Texture3D source, int newWidth, int newHeight, int newDepth)
{
    Texture3D result = new Texture3D(newWidth, newHeight, newDepth, TextureFormat.RGBA32, false);
    Color[] newColors = new Color[newWidth * newHeight * newDepth];

    Color[] sourceColors = source.GetPixels();
    int oldWidth = source.width;
    int oldHeight = source.height;
    int oldDepth = source.depth;

    RenderTexture rt = RenderTexture.GetTemporary(newWidth, newHeight, 0);
    RenderTexture.active = rt;

    // 用于临时传输贴图的 2D 贴图
    Texture2D tempSource2D = new Texture2D(oldWidth, oldHeight, TextureFormat.RGBA32, false);
    Texture2D tempResult2D = new Texture2D(newWidth, newHeight, TextureFormat.RGBA32, false);

    for (int z = 0; z < newDepth; z++)
    {
        // 源切片对应旧的 z（缩放到 0~1 再乘 oldDepth）
        float zRatio = (float)z / Mathf.Max(1, newDepth - 1);
        int oldZ = Mathf.Clamp(Mathf.RoundToInt(zRatio * (oldDepth - 1)), 0, oldDepth - 1);

        // 读取该层的颜色数据
        Color[] sliceColors = new Color[oldWidth * oldHeight];
        for (int y = 0; y < oldHeight; y++)
        {
            for (int x = 0; x < oldWidth; x++)
            {
                int srcIndex = x + y * oldWidth + oldZ * oldWidth * oldHeight;
                sliceColors[x + y * oldWidth] = sourceColors[srcIndex];
            }
        }

        tempSource2D.SetPixels(sliceColors);
        tempSource2D.Apply();

        // Blit 缩放
        Graphics.Blit(tempSource2D, rt);

        // 读取缩放后的颜色
        tempResult2D.ReadPixels(new Rect(0, 0, newWidth, newHeight), 0, 0);
        tempResult2D.Apply();

        Color[] resizedSlice = tempResult2D.GetPixels();

        // 写入结果 3D 贴图的对应层
        for (int y = 0; y < newHeight; y++)
        {
            for (int x = 0; x < newWidth; x++)
            {
                int dstIndex = x + y * newWidth + z * newWidth * newHeight;
                newColors[dstIndex] = resizedSlice[x + y * newWidth];
            }
        }
    }

    result.SetPixels(newColors);
    result.Apply();

    // 清理
    RenderTexture.ReleaseTemporary(rt);
    RenderTexture.active = null;

    return result;
}
    
    public static SphericalHarmonicsL2[,,] SampleSHVolumeGrid(Bounds bounds, float step)
    {
        var probes = LightmapSettings.lightProbes;
        var positions = probes.positions;
        var shs = probes.bakedProbes;

        var grid = new SphericalHarmonicsL2[100, 100, 100];

        for (int i = 0; i < positions.Length; i++)
        {
            Vector3 pos = positions[i];
            int x = Mathf.FloorToInt(pos.x);
            int y = Mathf.FloorToInt(pos.y);
            int z = Mathf.FloorToInt(pos.z);

            // 确保在合法范围内
            if (x >= 0 && x < 100 &&
                y >= 0 && y < 100 &&
                z >= 0 && z < 100)
            {
                grid[x, y, z] = shs[i];
            }
            else
            {
                grid[x, y, z] = new SphericalHarmonicsL2();
            }
        }

        FillMissingSHWithVisibility(grid, (from, to) =>
        {
            //return true;
            Vector3 dir = to - from; //+ Vector3.one * 0.1f
            return !Physics.Raycast(from , dir.normalized, dir.magnitude);
        });
        
        return grid;
    }

   
    public static void FillMissingSHWithVisibility(
        SphericalHarmonicsL2[,,] grid,
        Func<Vector3, Vector3, bool> IsVisible)
    {
        int sizeX = grid.GetLength(0);
        int sizeY = grid.GetLength(1);
        int sizeZ = grid.GetLength(2);

        for (int x = 0; x < sizeX; x++)
        for (int y = 0; y < sizeY; y++)
        for (int z = 0; z < sizeZ; z++)
        {
            if (!IsEmptySH(grid[x, y, z]))
                continue;

            Vector3 pos = new Vector3(x, y, z);
            SphericalHarmonicsL2 accum = new SphericalHarmonicsL2();
            float totalWeight = 0f;

            for (int dx = -2; dx <= 2; dx++)
            for (int dy = -2; dy <= 2; dy++)
            for (int dz = -2; dz <= 2; dz++)
            {
                int nx = x + dx;
                int ny = y + dy;
                int nz = z + dz;

                // 边界检测
                if (nx < 0 || ny < 0 || nz < 0 ||
                    nx >= sizeX || ny >= sizeY || nz >= sizeZ)
                    continue;

                // 仅考虑 probe 点（2米间隔）
                if (nx % 2 != 0 || ny % 2 != 0 || nz % 2 != 0)
                    continue;

                SphericalHarmonicsL2 probeSH = grid[nx, ny, nz];
                if (IsEmptySH(probeSH))
                    continue;

                Vector3 probePos = new Vector3(nx, ny, nz);
                if (!IsVisible(pos, probePos))
                    continue;

                float dist = Vector3.Distance(pos, probePos);
                float weight = 1f / Mathf.Max(dist, 0.001f);

                AddSH(ref accum, probeSH, weight);
                totalWeight += weight;
            }

            if (totalWeight > 0f)
            {
                NormalizeSH(ref accum, totalWeight);
                grid[x, y, z] = accum;
            }
        }
    }
    
    static bool IsEmptySH(SphericalHarmonicsL2 sh)
    {
        return Mathf.Approximately(sh[0, 0], 0f) &&
               Mathf.Approximately(sh[1, 0], 0f) &&
               Mathf.Approximately(sh[2, 0], 0f);
    }
    
    static void NormalizeSH(ref SphericalHarmonicsL2 sh, float totalWeight)
    {
        if (totalWeight <= 0f) return;
        for (int c = 0; c < 3; c++)
        for (int i = 0; i < 9; i++)
            sh[c, i] /= totalWeight;
    }
    
    // 3x3x3 高斯权重核（归一化系数为 1/64）
    static readonly float[,,] gaussianKernel = new float[,,]
    {
        {
            { 1, 2, 1 },
            { 2, 4, 2 },
            { 1, 2, 1 }
        },
        {
            { 2, 4, 2 },
            { 4, 8, 4 },
            { 2, 4, 2 }
        },
        {
            { 1, 2, 1 },
            { 2, 4, 2 },
            { 1, 2, 1 }
        }
    };

    const float normalization = 1f / 64f;

    /// <summary>
    /// 对体素网格做高斯滤波（平滑所有 SH 分量）
    /// </summary>
    public static SphericalHarmonicsL2[,,] SmoothSHVolume(SphericalHarmonicsL2[,,] volume, bool onlyL0 = false)
    {
        int sizeX = volume.GetLength(0);
        int sizeY = volume.GetLength(1);
        int sizeZ = volume.GetLength(2);

        var result = new SphericalHarmonicsL2[sizeX, sizeY, sizeZ];

        for (int x = 0; x < sizeX; x++)
        for (int y = 0; y < sizeY; y++)
        for (int z = 0; z < sizeZ; z++)
        {
            SphericalHarmonicsL2 accum = new SphericalHarmonicsL2();

            for (int dx = -1; dx <= 1; dx++)
            for (int dy = -1; dy <= 1; dy++)
            for (int dz = -1; dz <= 1; dz++)
            {
                int nx = x + dx;
                int ny = y + dy;
                int nz = z + dz;

                if (nx < 0 || nx >= sizeX ||
                    ny < 0 || ny >= sizeY ||
                    nz < 0 || nz >= sizeZ)
                    continue;

                float weight = gaussianKernel[dx + 1, dy + 1, dz + 1] * normalization;
                if (onlyL0)
                    AddSH_L0(ref accum, volume[nx, ny, nz], weight);
                else
                    AddSH(ref accum, volume[nx, ny, nz], weight);
            }

            result[x, y, z] = accum;
        }

        return result;
    }

    // 加权累加 SH 所有通道
    static void AddSH(ref SphericalHarmonicsL2 target, SphericalHarmonicsL2 source, float weight)
    {
        for (int rgb = 0; rgb < 3; rgb++)
        for (int i = 0; i < 9; i++)
            target[rgb, i] += source[rgb, i] * weight;
    }

    // 加权累加 SH 仅 L0 通道
    static void AddSH_L0(ref SphericalHarmonicsL2 target, SphericalHarmonicsL2 source, float weight)
    {
        for (int rgb = 0; rgb < 3; rgb++)
            target[rgb, 0] += source[rgb, 0] * weight;
    }    
    
    static void ScaleSH(ref SphericalHarmonicsL2 sh, float scale)
    {
        for (int rgb = 0; rgb < 3; rgb++)
        for (int i = 0; i < 9; i++)
            sh[rgb, i] *= scale;
    }
    
    static readonly float[] gaussian1D = new float[] { 1f, 4f, 6f, 4f, 1f }; // sum = 16
    static readonly float normalization_555 = 1f / 4096f; // 16 * 16 * 16 = 4096

    public static SphericalHarmonicsL2[,,] SmoothSHVolume555(SphericalHarmonicsL2[,,] volume, Func<Vector3, Vector3, bool> IsVisible, bool onlyL0 = false)
    {
        int sizeX = volume.GetLength(0);
        int sizeY = volume.GetLength(1);
        int sizeZ = volume.GetLength(2);

        var result = new SphericalHarmonicsL2[sizeX, sizeY, sizeZ];

        for (int x = 0; x < sizeX; x++)
        for (int y = 0; y < sizeY; y++)
        for (int z = 0; z < sizeZ; z++)
        {
            SphericalHarmonicsL2 accum = new SphericalHarmonicsL2();
            float totalWeight = 0f;

            Vector3 pos = new Vector3(x, y, z);
            
            for (int dx = -2; dx <= 2; dx++)
            for (int dy = -2; dy <= 2; dy++)
            for (int dz = -2; dz <= 2; dz++)
            {
                int nx = x + dx;
                int ny = y + dy;
                int nz = z + dz;

                if (nx < 0 || nx >= sizeX ||
                    ny < 0 || ny >= sizeY ||
                    nz < 0 || nz >= sizeZ)
                    continue;

                Vector3 probePos = new Vector3(nx, ny, nz);
                if (!IsVisible(pos,probePos))
                {
                    continue;
                }
                float wx = gaussian1D[dx + 2];
                float wy = gaussian1D[dy + 2];
                float wz = gaussian1D[dz + 2];
                float weight = wx * wy * wz;

                if (onlyL0)
                    AddSH_L0(ref accum, volume[nx, ny, nz], weight);
                else
                    AddSH(ref accum, volume[nx, ny, nz], weight);

                totalWeight += weight;
            }

            // 归一化
            if (totalWeight > 0f)
            {
                for (int c = 0; c < 3; c++)
                {
                    int maxCoeff = onlyL0 ? 1 : 9;
                    for (int i = 0; i < maxCoeff; i++)
                    {
                        accum[c, i] /= totalWeight;
                    }
                }
            }

            result[x, y, z] = accum;
        }

        return result;
    }
}
